{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# Parameterized Circuit"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "d44a6561b8406d49"
  },
  {
   "cell_type": "markdown",
   "source": [
    "Note: Running this notebook requires these additional dependencies: `tensorflow`, `matplotlib`"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "4d4be378ae0dd3c"
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "138c4fc25f044ef1",
   "metadata": {
    "collapsed": true,
    "ExecuteTime": {
     "end_time": "2024-01-25T08:33:28.053247400Z",
     "start_time": "2024-01-25T08:33:23.921657400Z"
    }
   },
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "from braandket import ArrayLike, tensorflow_backend\n",
    "from braandket_circuit import BnkRuntime, DM, H, QOperation, QParticle, Ry, Rz, allocate_qubit"
   ]
  },
  {
   "cell_type": "markdown",
   "source": [
    "We can define a parameterized circuit as below. Theoretically `Uzyz` have the ability to fit any single qubit unitary gate."
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "208449dda38f9b88"
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "outputs": [],
   "source": [
    "class Uzyz(QOperation):\n",
    "    def __init__(self, thetas: ArrayLike):\n",
    "        super().__init__()\n",
    "        self.thetas = thetas\n",
    "\n",
    "    def __call__(self, qubit: QParticle):\n",
    "        Rz(self.thetas[0])(qubit)\n",
    "        Ry(self.thetas[1])(qubit)\n",
    "        Rz(self.thetas[2])(qubit)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-25T08:33:28.053247400Z",
     "start_time": "2024-01-25T08:33:28.049530400Z"
    }
   },
   "id": "initial_id"
  },
  {
   "cell_type": "markdown",
   "source": [
    "Suppose we want to fit Hadamard gate with `Uzyz`. \n",
    "\n",
    "We can define a `circuit`, in which apply an `H` gate and then an `Uzyz` and then measure a qubit.\n",
    "\n",
    "If `Uzyz` fit `H` gate very well, the measure result probability will be `0` as the initial state.\n",
    "\n",
    "Here we use `DM(0)` instead of `M` as the measurement, to \"designate\" the measurement result (of cause, this can be done only on a simulator). That way we can conveniently get the probability of getting `0`."
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "a85f131f02366145"
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "outputs": [],
   "source": [
    "def circuit(thetas: ArrayLike, qubit: QParticle):\n",
    "    H(qubit)\n",
    "    Uzyz(thetas)(qubit)\n",
    "    return DM(0)(qubit)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-25T08:33:28.063696300Z",
     "start_time": "2024-01-25T08:33:28.053247400Z"
    }
   },
   "id": "6687c3a94b88beeb"
  },
  {
   "cell_type": "markdown",
   "source": [
    "Let's try running the `circuit` with arbitrarily filled `thetas`."
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "11bf5baa8cfc8acb"
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "result.value=0\n",
      "result.prob=array(0.40116159)\n"
     ]
    }
   ],
   "source": [
    "qubit = allocate_qubit()\n",
    "result = circuit([0.1, 0.2, 0.3], qubit)\n",
    "\n",
    "print(f\"{result.value=}\")\n",
    "print(f\"{result.prob=}\")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-25T08:33:28.064699400Z",
     "start_time": "2024-01-25T08:33:28.057741200Z"
    }
   },
   "id": "e2bf2909a0f36e87"
  },
  {
   "cell_type": "markdown",
   "source": [
    "Then we want to optimize the `thetas` so that the `Uzyz` fits `H` better. Here we use gradient descent as the optimization method, and we obtain the gradient with the help of TensorFlow.\n",
    "\n",
    "By default, this framework use numpy to perform the computation. To use tensorflow, we need to run the circuit within a \"tensorflow-backend runtime\". Correspondingly, the arguments (`thetas`) and returned values (`result.value` and `result.prob`) should all be `tf.Tensor`.\n",
    "\n",
    "In this case, we use `BnkRuntime(tensorflow_backend)` as the runtime. We can use `with` to conveniently switch the runtime. Note that the calling of `allocate_qubit` also needs to be wrapped inside because it involves the initialization of quantum state."
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "789770141f5c85bc"
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "result.value=<tf.Tensor: shape=(), dtype=int32, numpy=0>\n",
      "result.prob=<tf.Tensor: shape=(), dtype=float64, numpy=0.4999999999999999>\n"
     ]
    }
   ],
   "source": [
    "thetas = tf.Variable(tf.zeros(3, dtype=tf.float32))\n",
    "\n",
    "with BnkRuntime(tensorflow_backend):\n",
    "    qubit = allocate_qubit()\n",
    "    result = circuit(thetas, qubit)\n",
    "\n",
    "print(f\"{result.value=}\")\n",
    "print(f\"{result.prob=}\")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-25T08:33:28.188165900Z",
     "start_time": "2024-01-25T08:33:28.084695400Z"
    }
   },
   "id": "a231cf97f46b967f"
  },
  {
   "cell_type": "markdown",
   "source": [
    "To obtain the gradient with the help of TensorFlow, we use the `tf.GradientTape` (the officially recommended way)."
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "f52d81d9c52dcb60"
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:You are casting an input of type complex128 to an incompatible dtype float32.  This will discard the imaginary part and may not be what you intended.\n",
      "WARNING:tensorflow:You are casting an input of type complex64 to an incompatible dtype float32.  This will discard the imaginary part and may not be what you intended.\n",
      "WARNING:tensorflow:You are casting an input of type complex64 to an incompatible dtype float32.  This will discard the imaginary part and may not be what you intended.\n",
      "gradients=<tf.Tensor: shape=(3,), dtype=float32, numpy=array([ 0. , -0.5,  0. ], dtype=float32)>\n"
     ]
    }
   ],
   "source": [
    "thetas = tf.Variable(tf.zeros(3, dtype=tf.float32))\n",
    "\n",
    "with BnkRuntime(tensorflow_backend), tf.GradientTape() as tape:\n",
    "    qubit = allocate_qubit()\n",
    "    result = circuit(thetas, qubit)\n",
    "\n",
    "gradients = tape.gradient(result.prob, thetas)\n",
    "print(f\"{gradients=}\")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-25T08:33:28.252537200Z",
     "start_time": "2024-01-25T08:33:28.178166200Z"
    }
   },
   "id": "c42485aceb501f71"
  },
  {
   "cell_type": "markdown",
   "source": [
    "We can use many utils in tensorflow to do the optimization. \n",
    "\n",
    "In this case, we use:\n",
    "* `tf.keras.optimizers.Adam` as the optimizer.\n",
    "* `tf.keras.losses.binary_crossentropy` as the loss function.\n",
    "* `tf.function` to optimize the forward and backward process."
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "9fd9db26ccb91898"
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:You are casting an input of type complex128 to an incompatible dtype float32.  This will discard the imaginary part and may not be what you intended.\n",
      "WARNING:tensorflow:You are casting an input of type complex64 to an incompatible dtype float32.  This will discard the imaginary part and may not be what you intended.\n",
      "WARNING:tensorflow:You are casting an input of type complex64 to an incompatible dtype float32.  This will discard the imaginary part and may not be what you intended.\n",
      "WARNING:tensorflow:You are casting an input of type complex128 to an incompatible dtype float32.  This will discard the imaginary part and may not be what you intended.\n",
      "WARNING:tensorflow:You are casting an input of type complex64 to an incompatible dtype float32.  This will discard the imaginary part and may not be what you intended.\n",
      "WARNING:tensorflow:You are casting an input of type complex64 to an incompatible dtype float32.  This will discard the imaginary part and may not be what you intended.\n",
      "i=0, prob=0.50, loss=0.6931\n",
      "i=1, prob=0.51, loss=0.6733\n",
      "i=2, prob=0.52, loss=0.6539\n",
      "i=3, prob=0.53, loss=0.6350\n",
      "i=4, prob=0.54, loss=0.6164\n",
      "i=5, prob=0.55, loss=0.5982\n",
      "i=6, prob=0.56, loss=0.5804\n",
      "i=7, prob=0.57, loss=0.5630\n",
      "i=8, prob=0.58, loss=0.5459\n",
      "i=9, prob=0.59, loss=0.5293\n",
      "i=10, prob=0.60, loss=0.5131\n",
      "i=11, prob=0.61, loss=0.4972\n",
      "i=12, prob=0.62, loss=0.4818\n",
      "i=13, prob=0.63, loss=0.4667\n",
      "i=14, prob=0.64, loss=0.4519\n",
      "i=15, prob=0.65, loss=0.4376\n",
      "i=16, prob=0.65, loss=0.4236\n",
      "i=17, prob=0.66, loss=0.4099\n",
      "i=18, prob=0.67, loss=0.3967\n",
      "i=19, prob=0.68, loss=0.3837\n",
      "i=20, prob=0.69, loss=0.3711\n",
      "i=21, prob=0.70, loss=0.3589\n",
      "i=22, prob=0.71, loss=0.3469\n",
      "i=23, prob=0.72, loss=0.3353\n",
      "i=24, prob=0.72, loss=0.3240\n",
      "i=25, prob=0.73, loss=0.3131\n",
      "i=26, prob=0.74, loss=0.3024\n",
      "i=27, prob=0.75, loss=0.2921\n",
      "i=28, prob=0.75, loss=0.2820\n",
      "i=29, prob=0.76, loss=0.2722\n",
      "i=30, prob=0.77, loss=0.2627\n",
      "i=31, prob=0.78, loss=0.2535\n",
      "i=32, prob=0.78, loss=0.2446\n",
      "i=33, prob=0.79, loss=0.2359\n",
      "i=34, prob=0.80, loss=0.2275\n",
      "i=35, prob=0.80, loss=0.2194\n",
      "i=36, prob=0.81, loss=0.2115\n",
      "i=37, prob=0.82, loss=0.2038\n",
      "i=38, prob=0.82, loss=0.1964\n",
      "i=39, prob=0.83, loss=0.1892\n",
      "i=40, prob=0.83, loss=0.1822\n",
      "i=41, prob=0.84, loss=0.1754\n",
      "i=42, prob=0.84, loss=0.1689\n",
      "i=43, prob=0.85, loss=0.1626\n",
      "i=44, prob=0.86, loss=0.1564\n",
      "i=45, prob=0.86, loss=0.1505\n",
      "i=46, prob=0.87, loss=0.1448\n",
      "i=47, prob=0.87, loss=0.1392\n",
      "i=48, prob=0.87, loss=0.1339\n",
      "i=49, prob=0.88, loss=0.1287\n",
      "i=50, prob=0.88, loss=0.1237\n",
      "i=51, prob=0.89, loss=0.1188\n",
      "i=52, prob=0.89, loss=0.1141\n",
      "i=53, prob=0.90, loss=0.1096\n",
      "i=54, prob=0.90, loss=0.1052\n",
      "i=55, prob=0.90, loss=0.1010\n",
      "i=56, prob=0.91, loss=0.0969\n",
      "i=57, prob=0.91, loss=0.0930\n",
      "i=58, prob=0.91, loss=0.0892\n",
      "i=59, prob=0.92, loss=0.0856\n",
      "i=60, prob=0.92, loss=0.0820\n",
      "i=61, prob=0.92, loss=0.0786\n",
      "i=62, prob=0.93, loss=0.0753\n",
      "i=63, prob=0.93, loss=0.0722\n",
      "i=64, prob=0.93, loss=0.0691\n",
      "i=65, prob=0.94, loss=0.0662\n",
      "i=66, prob=0.94, loss=0.0634\n",
      "i=67, prob=0.94, loss=0.0606\n",
      "i=68, prob=0.94, loss=0.0580\n",
      "i=69, prob=0.95, loss=0.0555\n",
      "i=70, prob=0.95, loss=0.0531\n",
      "i=71, prob=0.95, loss=0.0507\n",
      "i=72, prob=0.95, loss=0.0485\n",
      "i=73, prob=0.95, loss=0.0463\n",
      "i=74, prob=0.96, loss=0.0442\n",
      "i=75, prob=0.96, loss=0.0422\n",
      "i=76, prob=0.96, loss=0.0403\n",
      "i=77, prob=0.96, loss=0.0385\n",
      "i=78, prob=0.96, loss=0.0367\n",
      "i=79, prob=0.97, loss=0.0350\n",
      "i=80, prob=0.97, loss=0.0334\n",
      "i=81, prob=0.97, loss=0.0318\n",
      "i=82, prob=0.97, loss=0.0303\n",
      "i=83, prob=0.97, loss=0.0289\n",
      "i=84, prob=0.97, loss=0.0275\n",
      "i=85, prob=0.97, loss=0.0262\n",
      "i=86, prob=0.98, loss=0.0249\n",
      "i=87, prob=0.98, loss=0.0237\n",
      "i=88, prob=0.98, loss=0.0226\n",
      "i=89, prob=0.98, loss=0.0215\n",
      "i=90, prob=0.98, loss=0.0204\n",
      "i=91, prob=0.98, loss=0.0194\n",
      "i=92, prob=0.98, loss=0.0184\n",
      "i=93, prob=0.98, loss=0.0175\n",
      "i=94, prob=0.98, loss=0.0166\n",
      "i=95, prob=0.98, loss=0.0157\n",
      "i=96, prob=0.99, loss=0.0149\n",
      "i=97, prob=0.99, loss=0.0142\n",
      "i=98, prob=0.99, loss=0.0134\n",
      "i=99, prob=0.99, loss=0.0127\n"
     ]
    },
    {
     "data": {
      "text/plain": "<Figure size 640x480 with 2 Axes>",
      "image/png": "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ahx77TjsteOw147DXjtOS3t9PeuKdiq4UqlEdHR0g91USUlJGDJkSJO3c/jwYQQEBNi7PCIiImqnRD0slZiYiIcffhgxMTGIjY3FqlWrkJmZienTpwOoPaSUk5ODdevWAag9myo8PBx9+vSByWTC+vXr8c033+Cbb74R82MQERFRGyJquJk0aRKKioowf/586HQ6REVFYfPmzQgLCwMA6HQ6ZGZm2pY3mUz4xz/+gZycHDg5OaFPnz746aefMHbsWLE+AhEREbUxok8oTkhIQEJCQqPvrV27tt7rF154AS+88IIDqiIiIqL2SvRw42iCIAC4vlnXTWU2m2EwGKDX6zlBrZWx147DXjsOe+047LXj2KvXdb+3636PX02nCzfl5eUAgJCQEJErISIioutVXl4ON7erX/lfIjQlAnUgVqsVubm5cHV1tfu9nuquoZOVlWX3a+hQfey147DXjsNeOw577Tj26rUgCCgvL0dgYCCk0quf7N3p9txIpVIEBwe36vfQarX8y+Ig7LXjsNeOw147DnvtOPbo9bX22NQR7To3RERERK2B4YaIiIg6FIYbO1KpVHj55Zcbvd0D2Rd77TjsteOw147DXjuOGL3udBOKiYiIqGPjnhsiIiLqUDrd2VKteSo4ERERtQ6eCn4Vubm5vIAfERFRO5WVlXXNS7p0unDj6uoKAK1y4Saz2YytW7ciPj6el/NuZey147DXjsNeOw577Tj26nXdxQDrfo9fTacLN3WHolrjwk1msxkajQZarZZ/WVoZe+047LXjsNeOw147jr173ZQpJZxQTERERB0Kww0RERF1KAw3RERE1KEw3BAREVGHwnBjRxsOZuNIEa+dQ0REJCaGGzvZda4A//o+FR+fk2LbmQKxyyEiIuq0GG7sZEhXb4zv6w+rIMHTXxxFclqh2CURERF1Sgw3diKTSrDonij09bDCVGPFtHUHkXKxWOyyiIiIOh2GGztSyKR4tIcVQ7t5wWCy4NE1f+BETpnYZREREXUqDDd2JpcCyx8cgBvDPVFurMHDq/fjTF652GURERF1Ggw3rcBJKcPqR2PQP8QdJQYzHvpwP9LyK8Qui4iIqFNguGklrmoF1j12I3oHaFFYYcTkD/Yho7BS7LKIiIg6PIabVuSmUWD9tJvQ088V+eW1ASer2CB2WURERB0aw00r83RWYv20m9DV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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "thetas = tf.Variable(tf.zeros(3, dtype=tf.float32))\n",
    "optimizer = tf.keras.optimizers.Adam(2e-2)\n",
    "\n",
    "\n",
    "@tf.function\n",
    "def forward_and_backward():\n",
    "    with BnkRuntime(tensorflow_backend), tf.GradientTape() as tape:\n",
    "        qubit = allocate_qubit()\n",
    "        result = circuit(thetas, qubit)\n",
    "        loss = tf.keras.losses.binary_crossentropy([1.0], [result.prob])\n",
    "\n",
    "    gradients = tape.gradient(loss, thetas)\n",
    "    optimizer.apply_gradients(zip([gradients], [thetas]))\n",
    "\n",
    "    return result.prob, loss\n",
    "\n",
    "\n",
    "logs_prob = []\n",
    "logs_loss = []\n",
    "for i in range(100):\n",
    "    prob, loss = forward_and_backward()\n",
    "\n",
    "    print(f\"{i=}, prob={float(prob):.2f}, loss={float(loss):.4f}\")\n",
    "    logs_prob.append(float(prob))\n",
    "    logs_loss.append(float(loss))\n",
    "\n",
    "fig, (axes0, axes1) = plt.subplots(2, 1, sharex='all')\n",
    "fig.subplots_adjust(hspace=0.05)\n",
    "axes0.plot(logs_prob)\n",
    "axes0.set_ylabel('prob')\n",
    "axes0.grid('all')\n",
    "axes1.plot(logs_loss)\n",
    "axes1.grid('all')\n",
    "axes1.set_ylabel('loss')\n",
    "axes1.set_xlabel('step')\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-01-25T08:33:31.196390400Z",
     "start_time": "2024-01-25T08:33:28.246536400Z"
    }
   },
   "id": "40e11f3d9211e35e"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
